package com.licq.ldoc.domain;

import com.licq.ldoc.domain.support.ToStringBehavior;

public abstract class AttributeType {
    public static final AttributeType INT = new DefaultAttributeType("int");
    public static final AttributeType LONG = new DefaultAttributeType("long");
    public static final AttributeType SHORT = new DefaultAttributeType("short");
    public static final AttributeType CHAR = new DefaultAttributeType("char");
    public static final AttributeType BOOLEAN = new DefaultAttributeType("boolean");
    public static final AttributeType STRING = new DefaultAttributeType("string");
    public static final AttributeType DOUBLE = new DefaultAttributeType("double");
    public static final AttributeType FLOAT = new DefaultAttributeType("float");
    public static final AttributeType BYTE = new DefaultAttributeType("byte");
    public static final AttributeType BIGDECIMAL = new DefaultAttributeType("bigdecimal", 
    		ToStringBehavior.decimal());
    public static final AttributeType BIGINTEGER = new DefaultAttributeType("bigint");
    public static final AttributeType DATE = new DefaultAttributeType("date",
    		ToStringBehavior.date());
    public static final AttributeType CALENDAR = new DefaultAttributeType("calendar",
    		ToStringBehavior.calendar());
    public static final AttributeType TIME = new DefaultAttributeType("time",
    		ToStringBehavior.date());
    public static final AttributeType TIMESTAMP = new DefaultAttributeType("timestamp",
    		ToStringBehavior.date());
    public static final AttributeType SQLDATE = new DefaultAttributeType("sqldate",
    		ToStringBehavior.date());
    public static final AttributeType CLASS = new DefaultAttributeType("class",
    		ToStringBehavior.clazz());
    public static final AttributeType ENUM = new DefaultAttributeType("enum");
    public static final AttributeType OBJECT = new DefaultAttributeType("object");
    
	public abstract String getLabel();
	
	public abstract String toString(Object obj);
	
	public static boolean isWrapperType(AttributeType type) {
		return CollectionAttributeType.class.isInstance(type)
			|| ArrayAttributeType.class.isInstance(type);
	}
	
	public static boolean isMapperType(AttributeType type) {
		return MapAttributeType.class.isInstance(type);
	}
	
	public static boolean isNumberType(AttributeType type) {
		return AttributeType.INT == type || AttributeType.LONG == type
			|| AttributeType.SHORT == type || AttributeType.DOUBLE == type
			|| AttributeType.FLOAT == type || AttributeType.BYTE == type
			|| AttributeType.BIGDECIMAL == type || AttributeType.BIGINTEGER == type;
	}
	
		
	public static boolean isDateType(AttributeType type) {
		return AttributeType.DATE == type || AttributeType.CALENDAR == type
			|| AttributeType.SQLDATE == type || AttributeType.TIME == type
			|| AttributeType.TIMESTAMP == type;
	}
	
	static class DefaultAttributeType extends AttributeType {
		private String label;
		
		private ToStringBehavior toStringBehavior;

		private DefaultAttributeType(String label, ToStringBehavior toStringBehavior) {
			this.label = label;
			this.toStringBehavior = toStringBehavior;
		}
		
		public DefaultAttributeType(String label) {
			this(label, ToStringBehavior.defaut());
		}
		
		@Override
		public String getLabel() {
			return label;
		}
		
		@Override
		public String toString(Object obj) {
			return toStringBehavior.toString(obj);
		}
		
	}
	
	public abstract static class AttributeTypeDecorator extends AttributeType {
		protected AttributeType attributeType;

		protected AttributeTypeDecorator(AttributeType attributeType) {
			this.attributeType = attributeType;
		}
		
		public AttributeType getAttributeType() {
			return attributeType;
		}

		public abstract String getLabel();
		
		@Override
		public String toString(Object obj) {
			throw new UnsupportedOperationException();
		}
	}
	
	public static class CollectionAttributeType extends AttributeTypeDecorator {

		public CollectionAttributeType(AttributeType attributeType) {
			super(attributeType);
		}
		
		@Override
		public String getLabel() {
			return "<" + attributeType.getLabel() + ">";
		}
	}
	
	public static class ArrayAttributeType extends AttributeTypeDecorator {
		
		public ArrayAttributeType(AttributeType attributeType) {
			super(attributeType);
		}
		
		@Override
		public String getLabel() {
			return "[" + attributeType.getLabel() + "]";
		}
		
	}
	
	public static class MapAttributeType extends AttributeType {
		
		private AttributeType keyAttributeType;
		
		private AttributeType valueAttributeType;
		
		public MapAttributeType(AttributeType keyAttributeType, 
				AttributeType valueAttributeType) {
			this.keyAttributeType = keyAttributeType;
			this.valueAttributeType = valueAttributeType;
		}
		
		@Override
		public String getLabel() {
			return String.format("<%s,%s>", keyAttributeType.getLabel(),
					valueAttributeType.getLabel());
		}
		
		public AttributeType getKeyAttributeType() {
			return keyAttributeType;
		}

		public AttributeType getValueAttributeType() {
			return valueAttributeType;
		}

		@Override
		public String toString(Object obj) {
			throw new UnsupportedOperationException();
		}
		
	}
	
}
